/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ru.example.netcracker.model.observer;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import ru.example.netcracker.model.ModelFacade;
import ru.example.netcracker.model.Scheduler;
import ru.example.netcracker.model.SynchronizedTaskGroup;
import ru.example.netcracker.model.TaskFactory;
import ru.example.netcracker.model.TaskProvider;
import ru.example.netcracker.model.entity.Alarm;
import ru.example.netcracker.model.entity.Contact;
import ru.example.netcracker.model.entity.Notification;
import ru.example.netcracker.model.entity.Task;
import ru.example.netcracker.model.entity.TaskGroup;

/**
 *
 * @author Nikolay
 */
public class TaskManagerModel implements ModelFacade, Observable {

    private static final Logger LOG = Logger.getLogger(TaskManagerModel.class.getName()); 
    
    private final List<Observer> observers;
    private Scheduler tasks;
    private TaskProvider taskProvider;
    private boolean changed;

    public TaskManagerModel(Scheduler tasks) {
        this.tasks = SynchronizedTaskGroup.SynchronizedScheduler(tasks);
        observers = new ArrayList<>();
    }

    public TaskManagerModel() {
        //TaskGroup loadTasks = loadTasks();
        tasks = SynchronizedTaskGroup.SynchronizedScheduler(new TaskGroup());
        observers = new ArrayList<>();
    }

    @Override
    synchronized public void addObserver(Observer o) {
        if (o != null) {
            LOG.log(Level.INFO, "Observer class: " + o.getClass());
            if (!observers.contains(o)) {
                observers.add(o);
            }
        } else {
            LOG.log(Level.ERROR, "Observer is null");
            throw new NullPointerException("Observer is null");
        }        
    }

    @Override
    synchronized public void deleteObserver(Observer o) {
        if (o != null) {
            LOG.log(Level.INFO, "Observer class: " + o.getClass());
            observers.remove(o);
        } else {
            LOG.log(Level.WARN, "Observer is null");
            //throw new NullPointerException("Observer is null");
        }
    }

    @Override
    synchronized public void notifyObservers() {
        LOG.log(Level.INFO, "notifyObservers");
        List<Observer> observersLocal = null;
        if (!changed) {
            return;
        }
        observersLocal = new ArrayList<>(this.observers);
        changed = false;
        for (Observer o : observersLocal) {
            o.update(this, new ArrayList<>(tasks.getTasks()));
        }
    }

    @Override
    public List<Task> getTasks() {
        LOG.log(Level.INFO, "getTasks");
        return tasks.getTasks();
    }

    @Override
    public Task getTask(String id) {
        LOG.log(Level.INFO, "id: " + id);
        if (id != null) {
            for (int i = 0; i < tasks.size(); i++) {//Task task : tasks) {
                Task task = tasks.getTask(i);
                if (task.getId().equals(id)) {
                    return task;
                }
            }
        } else {
            LOG.log(Level.WARN, "id is null");
            //throw new NullPointerException("id is null");
        }
        return null;
    }
    
    @Override
    public TaskProvider getTaskProvider() {
        return taskProvider;
    }

    @Override
    public void setTaskProvider(TaskProvider provider) {
        taskProvider = provider;
    }
    
    private String createTaskID() {
        UUID randomUUID = UUID.randomUUID();
        LOG.log(Level.INFO, "UUID: " + randomUUID);
        return randomUUID.toString();
    }
    
    @Override
    public boolean createNotification(String name, String description, Date date) {
        LOG.log(Level.INFO, "name: " + name + " description: " + description + " date: " + date);
        String id = createTaskID();
        if (date == null) {
            LOG.log(Level.WARN, "date is null");
            return false;
            //throw new NullPointerException("date is null");
        }
        if (date.getTime() - System.currentTimeMillis() <= 0) {
            LOG.log(Level.WARN, "task date <= current date");
            return false;
            //throw new IllegalArgumentException("task date <= current date");
        }
        Notification notification = TaskFactory.createNotification(id, name, description, date);
        tasks.addTask(notification);
        changed = true;
        notifyObservers();
        return true;
    }

    @Override
    public boolean createNotification(String name, String description, Date date, List<Contact> contacts) {
        LOG.log(Level.INFO, "name: " + name + " description: " + description + " date: " + date + " contacts: " + contacts);
        String id = createTaskID();
        if (date == null) {
            LOG.log(Level.WARN, "date is null");
            return false;
            //throw new NullPointerException("date is null");
        }
        if (date.getTime() - System.currentTimeMillis() <= 0) {
            LOG.log(Level.WARN, "task date <= current date");
            return false;
            //throw new IllegalArgumentException("task date <= current date");
        }
        Notification notification = TaskFactory.createNotification(id, name, description, date, contacts);
        tasks.addTask(notification);
        changed = true;
        notifyObservers();
        return true;
    }
    
    @Override
    public boolean createAlarm(String name, String path, Date date) {
        LOG.log(Level.INFO, "name: " + name + " path: " + path + " date: " + date);
        String id = createTaskID();
        if (date == null) {
            LOG.log(Level.WARN, "date is null");
            return false;
            //throw new NullPointerException("date is null");
        }
        if (date.getTime() - System.currentTimeMillis() <= 0) {
            LOG.log(Level.WARN, "task date <= current date");
            return false;
            //throw new IllegalArgumentException("task date <= current date");
        }
        Alarm alarm = TaskFactory.createAlarm(id, name, path, date);
        tasks.addTask(alarm);
        changed = true;
        notifyObservers();
        return true;
    }

    @Override
    public boolean updateNotification(String id, String name, String description, Date date, List<Contact> contacts) {
        LOG.log(Level.INFO, "id: " + id + " name: " + name + " description: " + description +
                " date: " + date + " contacts: " + contacts);
        if (id == null) {
            LOG.log(Level.WARN, "id is null");
            return false;
            //throw new NullPointerException("id is null");
        }
        Task task = getTask(id);
        if (task == null) {
            LOG.log(Level.WARN, "no tasks by id");
            return false;
            //throw new NullPointerException("no tasks by id");
        }
        if(!(task instanceof Notification)) {
            LOG.log(Level.WARN, "class not Notification");
            return false;
            //throw new ClassCastException("class not Notification");
        }
        Notification notif = (Notification) task;
        if (name != null) {
            notif.setName(name);
        }
        if (description != null) {
            notif.setDescription(description);
        }
        if (date != null) {
            if (date.getTime() - System.currentTimeMillis() <= 0) {
                LOG.log(Level.WARN, "task date <= current date");
                return false;
                //throw new IllegalArgumentException("task date <= current date");
            }
            notif.setDate(date);
        }
        notif.setShow(false);
        //if (contacts != null) {
        //    task.setContacts(contacts);
        //}
        changed = true;
        notifyObservers();
        return true;
    }
    
    @Override
    public boolean updateAlarm(String id, String name, String path, Date date) {
        LOG.log(Level.INFO, "id: " + id + " name: " + name + " path: " + path + " date: " + date);
        if (id == null) {
            LOG.log(Level.WARN, "id is null");
            return false;
            //throw new NullPointerException("id is null");
        }
        Task task = getTask(id);
        if (task == null) {
            LOG.log(Level.WARN, "no tasks by id");
            return false;
            //throw new NullPointerException("no tasks by id");
        }
        if(!(task instanceof Alarm)) {
            LOG.log(Level.WARN, "class not Alarm");
            return false;
            //throw new ClassCastException("class not Alarm");
        }
        Alarm alarm = (Alarm) task;
        if (name != null) {
            alarm.setName(name);
        }
        if (path != null) {
            alarm.setPath(path);
        }
        if (date != null) {
            if (date.getTime() - System.currentTimeMillis() <= 0) {
                LOG.log(Level.WARN, "task date <= current date");
                return false;
                //throw new IllegalArgumentException("task date <= current date");
            }
            alarm.setDate(date);
        }
        alarm.setShow(false);
        changed = true;
        notifyObservers();
        return true;
    }

    @Override
    public boolean deleteTask(String id) {
        LOG.log(Level.INFO, "id: " + id);
        if (id == null) {
            LOG.log(Level.WARN, "id is null");
            return false;
            //throw new NullPointerException("id is null");
        }
        Task task = getTask(id);
        if (task == null) {
            LOG.log(Level.WARN, "no tasks by id");
            return false;
            //throw new NullPointerException("no tasks by id");
        }
        tasks.removeTask(task);
        changed = true;
        notifyObservers();
        return true;
    }

    @Override
    public void loadTasks() {
        LOG.log(Level.INFO, "loadTasks from XML or DB");
        if(taskProvider != null) {
            Scheduler t = taskProvider.loadTasks();
            tasks = SynchronizedTaskGroup.SynchronizedScheduler(t);
        } else {
            LOG.log(Level.WARN, "Task Provider is null");
        }
    }

    @Override
    public void saveTasks() {
        LOG.log(Level.INFO, "saveTasks to XML or DB");
        if(taskProvider != null) {
            taskProvider.saveTasks(tasks);
        } else {
            LOG.log(Level.WARN, "Task Provider is null");
        }
    }
}
